package Api;

import Util.HttpUtil;
import Util.Tools;

import java.io.IOException;
import java.util.*;

/**
 * 汇付宝退款接口调用类
 * 支持单笔全额退款（agent_bill_id）和批量部分退款（refund_details）
 *
 * 返回示例:
 * <?xml version="1.0" encoding="utf-8"?>
 * <root>
 *   <ret_code>0000</ret_code>
 *   <ret_msg>退款成功</ret_msg>
 *   <agent_id>1664502</agent_id>
 *   <sign>...</sign>
 * </root>
 */
public class PaymentRefund {

    // 接口配置
    private static final String REFUND_URL = "https://pay.heepay.com/API/Payment/PaymentRefund.aspx";
    private static final String VERSION = "1";
    private static final String SIGN_TYPE = "MD5";

    public static void main(String[] args) {
        try {
            // ========== 配置参数 ==========
            String key = "1C3A345D4F1E48B7A02B90AC";           // 退款专用密钥（不要与支付密钥混淆）
            String agentId = "1664502";                        // 商户编号
            String notifyUrl = "https://www.heepay.com/refundCallBack"; // 异步通知地址

            // ========== 选择退款方式 ==========
            // 方式1：单笔退款
            //String agentBillId = "20250806175956";
            //String refundDetails = null;

            // 方式2：批量退款（示例）
             String agentBillId = null;
             String refundDetails = "20250806175956,0.01,123123111";

            // ========== 执行退款 ==========
            String response = executeRefund(key, agentId, notifyUrl, agentBillId, refundDetails);
            System.out.println("✅ 退款请求完成（并不代表退款成功）");

        } catch (IllegalArgumentException e) {
            System.err.println("❌ 参数错误: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("❌ 网络请求失败: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.err.println("❌ 未知错误: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 执行退款并处理响应
     *
     * @param key             退款密钥（后台获取）
     * @param agentId         商户编号
     * @param notifyUrl       异步通知地址
     * @param agentBillId     单笔退款订单号（可为空）
     * @param refundDetails   批量退款详情（格式：id,amount,reason|...）
     * @return 原始响应 XML
     * @throws Exception 网络或业务异常
     */
    public static String executeRefund(
            String key,
            String agentId,
            String notifyUrl,
            String agentBillId,
            String refundDetails
    ) throws Exception {

        validateParameters(key, agentId, notifyUrl, agentBillId, refundDetails);

        // 构建待签名字符串（注意：key 放最后）
        String rawSignString = buildSignString(agentId, agentBillId, notifyUrl, refundDetails, key);
        System.out.println("📝 待签名字符串: " + rawSignString);

        // 生成签名
        String sign = Tools.getMD5(rawSignString);
        System.out.println("🔐 生成签名: " + sign);

        // 构建请求参数（不含 key）
        Map<String, String> params = buildRequestParams(agentId, agentBillId, notifyUrl, refundDetails, sign);
        System.out.println("📤 请求参数: " + params); // 👈 打印真实请求参数

        // 发送请求
        String response = HttpUtil.sendPost1(REFUND_URL, params);
        System.out.println("📨 退款响应: " + response);

        // 解析并验签
        verifyResponse(response, key, agentId, sign);

        return response;
    }

    // ------------------ 私有方法 ------------------

    /**
     * 参数校验
     */
    private static void validateParameters(
            String key, String agentId, String notifyUrl,
            String agentBillId, String refundDetails) {

        Objects.requireNonNull(key, "退款密钥不能为空");
        if (key.trim().isEmpty()) throw new IllegalArgumentException("退款密钥不能为空");

        Objects.requireNonNull(agentId, "商户编号不能为空");
        if (agentId.trim().isEmpty()) throw new IllegalArgumentException("商户编号不能为空");

        Objects.requireNonNull(notifyUrl, "通知地址不能为空");
        if (notifyUrl.trim().isEmpty()) throw new IllegalArgumentException("通知地址不能为空");

        boolean hasAgentBillId = isNotBlank(agentBillId);
        boolean hasRefundDetails = isNotBlank(refundDetails);

        if (!hasAgentBillId && !hasRefundDetails) {
            throw new IllegalArgumentException("agent_bill_id 和 refund_details 不能同时为空");
        }
        if (hasAgentBillId && hasRefundDetails) {
            throw new IllegalArgumentException("agent_bill_id 和 refund_details 不能同时存在");
        }
    }

    /**
     * 构建用于签名的字符串（所有参数拼接，转小写）
     */
    private static String buildSignString(
            String agentId, String agentBillId, String notifyUrl,
            String refundDetails, String key) {

        List<String> pairs = new ArrayList<>();

        if (isNotBlank(agentBillId)) {
            pairs.add("agent_bill_id=" + agentBillId.trim());
        }
        pairs.add("agent_id=" + agentId);
        pairs.add("key=" + key); // key 参与签名
        pairs.add("notify_url=" + notifyUrl.trim());
        if (isNotBlank(refundDetails)) {
            pairs.add("refund_details=" + refundDetails.trim());
        }
        pairs.add("version=" + VERSION);

        return String.join("&", pairs).toLowerCase();
    }

    /**
     * 构建实际请求参数（不包含 key）
     */
    private static Map<String, String> buildRequestParams(
            String agentId, String agentBillId, String notifyUrl,
            String refundDetails, String sign) {

        Map<String, String> params = new HashMap<>();
        params.put("version", VERSION);
        params.put("agent_id", agentId);
        params.put("notify_url", notifyUrl);
        params.put("sign_type", SIGN_TYPE);
        params.put("sign", sign);

        if (isNotBlank(agentBillId)) {
            params.put("agent_bill_id", agentBillId);
        }
        if (isNotBlank(refundDetails)) {
            params.put("refund_details", refundDetails);
        }

        return params;
    }

    /**
     * 解析响应并验签
     */
    private static void verifyResponse(String response, String key, String agentId, String requestSign) {
        try {
            Map<String, String> resultMap = Tools.parseXml(response);
            String retCode = resultMap.get("ret_code");
            String retMsg = resultMap.get("ret_msg");
            String responseSign = resultMap.get("sign");

            if (retCode == null) {
                System.out.println("❌ 响应解析失败：ret_code 为空");
                return;
            }

            if (!"0000".equals(retCode)) {
                System.out.println("❌ 退款失败: " + retMsg);
                return;
            }

            System.out.println("✅ 退款成功: " + retMsg);

            // ✅ 验签：使用文档要求的字段
            String verifyData = String.format("agent_id=%s&key=%s&ret_code=%s&ret_msg=%s", agentId, key, retCode, retMsg).toLowerCase();
            System.out.println("🔍 待验签字符串: " + verifyData);

            String expectedSign = Tools.getMD5(verifyData);

            if (responseSign != null && responseSign.equalsIgnoreCase(expectedSign)) {
                System.out.println("✅ 同步验签结果: 成功");
            } else {
                System.out.println("❌ 同步验签结果: 失败");
                System.out.println("   期望签名: " + expectedSign);
                System.out.println("   实际签名: " + responseSign);
            }

        } catch (Exception e) {
            System.err.println("❌ 响应解析或验签异常: " + e.getMessage());
        }
    }

    /**
     * 判断字符串非空且非空白
     */
    private static boolean isNotBlank(String str) {
        return str != null && !str.trim().isEmpty();
    }
}